﻿using System;
using System.Collections.ObjectModel;
using System.IO;
using System.Linq;
using System.Threading;
using DynamicData;
using Microsoft.EntityFrameworkCore;
using MusicDiary.Lib.Models;
using MusicDiary.Lib.Service;
using MusicDiary.Lib.Vlc;
using MusicPlayer.Data;

namespace MusicDiary.WindowApp.ViewModels;

public class MainWindowViewModel : ViewModelBase
{

    #region MusicPlayer.Data

    private PlayerContext Context { get; }
    public ObservableCollection<MusicModel> Histories { get; set; }

    public ObservableCollection<PlaylistModel> Playlists { get; set; }

    #region DataOpera

    public void Add(PlaylistModel model)
    {
        Context.Playlists.Add(DataService.ToPlaylistData(model));
        Context.SaveChanges();
        Playlists.Add(model);
    }

    public async void Add(MusicModel model)
    {
        if (await Context.History.AnyAsync(x => x.Url == model.Url)) return;
        Context.History.Add(DataService.ToHistoryData(model));
        await Context.SaveChangesAsync();
        Histories.Add(model);
    }

    public void AddMusicToPlaylist(PlaylistModel playlistModel, MusicModel model)
    {
        var a = Context.Playlists.FirstOrDefault(x => x.Key == playlistModel.Key);
        if (a == null) return;
        a.Musics.Add(DataService.ToMusicData(model));
        Context.SaveChanges();
        Playlists.Clear();
        Playlists.Add(Context
            .Playlists
            .Include(x => x.Musics)
            .AsSplitQuery()
            .ToList()
            .Select(DataService.ToPlaylist));
    }

    public void RemoveList(PlaylistModel model)
    {
        Context.Playlists.Remove(DataService.ToPlaylistData(model));
        Context.SaveChanges();
        Playlists.Remove(model);
    }

    public async void RemoveHistory(MusicModel model)
    {
        var his = await Context.History.FirstOrDefaultAsync(x => x.Url == model.Url);
        if (his == null) return;
        Context.History.Remove(his);
        await Context.SaveChangesAsync();
        Histories.Remove(model);
    }

    #endregion

    #endregion

    #region This Playlist

    public ObservableCollection<MusicModel> Playlist { get; set; } = new();

    /// <summary>
    /// the pos of the music in the playlist
    /// </summary>
    private int Index { get; set; }

    #endregion

    #region Playing

    private MusicModel? _playingMusic;

    public MusicModel? PlayingMusic
    {
        get => _playingMusic;
        private set
        {
            if (value == null) return;
            SetField(ref _playingMusic, value);
            Player.Play(new Uri(value.Url));
            Add(value);
        }
    }

    private float _pos;

    public float Pos
    {
        get => _pos;
        private set => SetField(ref _pos, value);
    }

    private string _playState = "Play";

    public string PlayState
    {
        get => _playState;
        set
        {
            SetField(ref _playState, value);
            if (value == "Pause")
                Player.Pause();
            else
                Player.Play();
        }
    }

    #endregion

    #region PlayerSetting

    private string _mode = "List";

    public string Mode
    {
        get => _mode;
        set => SetField(ref _mode, value);
    }

    /// <summary>
    /// Mode change : Single -> Single Loop -> List -> Random -> Single
    /// </summary>
    public void ModeChange()
    {
        switch (Mode)
        {
            case "Single":
                Mode = "Single Loop";
                return;
            case "Single Loop":
                Mode = "List";
                return;
            case "List":
                Mode = "Random";
                return;
            case "Random":
                Mode = "Single";
                break;
        }
    }

    private VlcModel Player { get; set; }

    #endregion

    #region Func

    public void Next()
    {
        switch (Mode)
        {
            case "Single" or "":
                return;
            case "Single Loop":
                break;
            case "List":
                Index++;
                if (Index >= Playlist.Count)
                    Index = 0;
                break;
            case "Random":
                var random = new Random();
                var ran = random.Next(0, Playlist.Count - 1);
                Index = ran == Index ? Index + 1 : ran;
                if (Index >= Playlist.Count || Index <= 0)
                    Index = 0;
                break;
        }

        var s = Playlist[Index].Url;
        ThreadPool.QueueUserWorkItem(_ => PlayChange(s));
        Pos = 0;
    }

    public void PlayChange(string url)
    {
        if (!File.Exists(url)) return;
        PlayingMusic = new MusicModel(url);
    }
    
    public void Play()
    {
        PlayState = PlayState == "Play" ? "Pause" : "Play";
    }

    public void PlaylistChange(bool isOpenFromFinder = true)
    {
        if (PlayingMusic == null) return;

        if (!isOpenFromFinder) return;
        var path = Path.GetDirectoryName(PlayingMusic.Url);
        if (path == null) return;
        var dir = new DirectoryInfo(path);
        if (!dir.Exists) return;
        Playlist.Clear();
        Playlist.Add(dir.GetFiles()
            .Where(x => x.Extension is ".mp3" or ".flac")
            .Select(x => new MusicModel(x.FullName)));
        var model = Playlist.FirstOrDefault(x => x.Url == PlayingMusic.Url);
        if (model != null) Index = Playlist.IndexOf(model);
    }

    public void PosChange(float pos)
    {
        if (Math.Abs(Pos - pos) < 0.01) return;
        Player.PosChange(pos);
    }

    #endregion

    #region Init

    public MainWindowViewModel()
    {
        Context = new PlayerContext();
        Context.Init();
        Player = new VlcModel();
        Player.PosFlush += f =>
            ThreadPool.QueueUserWorkItem(_ => Pos = f);
        Player.Done += Next;

        Playlists = new ObservableCollection<PlaylistModel>(
            Context
                .Playlists
                .Include(x => x.Musics)
                .AsSplitQuery()
                .ToList()
                .Select(DataService.ToPlaylist)
        );
        Histories = new ObservableCollection<MusicModel>(
            Context.History
                .ToList()
                .Select(DataService.ToMusic)
        );
    }

    #endregion
}